/*
 * @Author: huangwei
 * @Date: 2021-05-21 14:57:01
 * @LastEditors: 黄威
 * @LastEditTime: 2021-12-22 18:10:24
 * @Description: 服务创建
 */
import type { AxiosRequestConfig } from 'axios'
import type { Service } from 'types'

// 地图相关
import L from 'leaflet'
import * as Cesium from 'Cesium'
// @ts-ignore
import { tiledMapLayer, wmtsLayer } from '@supermap/iclient-leaflet'

import settings from '@/settings'
import { Error } from '@/log'

// 元数据解析相关
import { parseStringPromise } from 'xml2js'
import { pureRequest } from './request'

// xml -> json
async function xml2Json(xml: string) {
    return parseStringPromise(xml, {
        explicitArray: false,
        mergeAttrs: true,
        normalizeTags: true,
        explicitRoot: false,
        tagNameProcessors: [tagName => tagName.replace('ows:', '')],
    })
}

// 获取系统元数据
async function getMetaData(
    svcMDUrl: string,
    config?: AxiosRequestConfig
): Promise<any> {
    return pureRequest.get(svcMDUrl, config).then(({ data }) => xml2Json(data))
}

// 求最大公约数
function getBigFactor(a: number, b: number): number {
    if (b == 0) {
        return a
    }
    return getBigFactor(b, a % b)
}

// 目前支持的服务
const SERVICES: Service[] = [
    {
        key: 'wms',
        rule: /(wms)|(^3$)/i,
        async getMDObj(url): Promise<L.WMSOptions> {
            return getMetaData(url, {
                params: {
                    SERVICE: 'WMS',
                    REQUEST: 'GetCapabilities',
                },
            }).then(obj => {
                // 解析包含的图层
                let layers = obj.capability.layer
                if (!Array.isArray(layers)) layers = [layers]

                return {
                    layers: layers.map((l: any) => l.name || l.title).join(','),
                }
            })
        },
        createLayer(svcUrl: string, props: L.WMSOptions = {}): L.TileLayer {
            return L.tileLayer.wms(svcUrl, props)
        },
        createLayer3d(
            svcUrl: string,
            props: L.WMSOptions = {}
        ): Cesium.ImageryLayer {
            // 添加默认配置
            const defaultOptions = {
                styles: '',
                format: 'image/jpeg',
                transparent: false,
            }
            props = Object.assign(defaultOptions, props)

            const imgProvider = new Cesium.WebMapServiceImageryProvider({
                url: svcUrl,
                layers: props.layers!,
                parameters: {
                    format: props.format,
                    styles: props.styles,
                    transparent: props.transparent,
                },
            })
            return new Cesium.ImageryLayer(imgProvider, {
                alpha: props.opacity ?? 1,
            })
        },
    },
    {
        key: 'wmts',
        rule: /(wmts)|(^2$)/i,
        /*
        {
            version: '1.0.0';
            style: '';
            tilematrixSet: '';
            format: 'image/png';
            tileSize: 256;
            matrixIds: null;
            layer: '';
            requestEncoding: 'KVP';
            attribution: '';
            noWrap: true;
        } 
        */
        async getMDObj(url) {
            return getMetaData(url, {
                params: {
                    SERVICE: 'WMTS',
                    REQUEST: 'GetCapabilities',
                },
            }).then(obj => {
                // 取第一个图层
                let layer = obj.contents.layer
                if (Array.isArray(layer)) layer = layer[0]

                // 取得style
                let style = layer.style
                if (Array.isArray(style)) {
                    style = style.find(s => s.isDefault == 'true') || style[0]
                }

                // 矩阵集
                let tilematrixset = obj.contents.tilematrixset
                if (Array.isArray(tilematrixset)) {
                    tilematrixset = tilematrixset[0]
                }

                return {
                    layer: layer.identifier || layer.title,
                    style: layer.style.identifier,
                    format: layer.format,
                    tilematrixSet: tilematrixset.identifier,
                    matrixIds: tilematrixset.tilematrix,
                }
            })
        },
        createLayer(svcUrl: string, props: any = {}): L.TileLayer {
            return wmtsLayer(svcUrl, props)
        },
        createLayer3d(svcUrl: string, props: any = {}): Cesium.ImageryLayer {
            let tileMatrixLabels = undefined,
                tilingScheme = undefined

            if (
                props.matrixIds &&
                Array.isArray(props.matrixIds) &&
                !props.tilingScheme
            ) {
                tileMatrixLabels = props.matrixIds.map(
                    (m: any) => +m.identifier
                )

                const matrixId = props.matrixIds[0]
                const x = +matrixId.matrixwidth,
                    y = +matrixId.matrixheight,
                    f = getBigFactor(x, y)
                tilingScheme = new Cesium.GeographicTilingScheme({
                    numberOfLevelZeroTilesX: x / f,
                    numberOfLevelZeroTilesY: y / f,
                })
            }

            const imgProvider = new Cesium.WebMapTileServiceImageryProvider({
                url: svcUrl,
                layer: props.layer,
                tileMatrixSetID: props.tilematrixSet || props.tileMatrixSetID,
                format: props.format,
                style: props.style,
                tileMatrixLabels: tileMatrixLabels || props.tileMatrixLabels,
                tilingScheme: tilingScheme || props.tilingScheme,
            })
            return new Cesium.ImageryLayer(imgProvider, {
                alpha: props.opacity ?? 1,
            })
        },
    },
    {
        key: 'smrestmap',
        rule: /(smrestmap)|(^1$)/i,
        /*
        {
            //如果有layersID，则是在使用专题图
            layersID: null,
            //如果为 true，则将请求重定向到瓦片的真实地址；如果为 false，则响应体中是瓦片的字节流
            redirect: false,
            transparent: true,
            cacheEnabled: true,
            clipReionEnabled: false,
            //地图显示裁剪的区域
            clipReion: null,
            //请求的地图的坐标参考系统。 如：prjCoordSys={"epsgCode":3857}
            prjCoordSys: null,
            //地图对象在同一范围内时，是否重叠显示
            overlapDisplayed: false,
            //避免地图对象压盖显示的过滤选项
            overlapDisplayedOptions: null,
            //切片版本名称，cacheEnabled 为 true 时有效。
            tileversion: null,
            crs: null,
            format: 'png',
            //启用托管地址。
            tileProxy:null,
            attribution: Attributions.Common.attribution,
            subdomains: null
        }
        */
        async getMDObj(url) {
            return getMetaData(url + '.xml').then(obj => ({
                prjCoordSys: { epsgCode: obj.prjcoordsys.epsgcode },
            }))
        },
        createLayer(svcUrl: string, props: any = {}): L.TileLayer {
            return tiledMapLayer(svcUrl, props)
        },
        createLayer3d(svcUrl: string, props: any = {}): Cesium.ImageryLayer {
            return new Cesium.ImageryLayer(
                new (Cesium as any).SuperMapImageryProvider({
                    url: svcUrl,
                    isTileMap: true,
                }),
                {
                    alpha: props.opacity ?? 1,
                }
            )
        },
    },
    {
        /**
         * url example:
         * https://services.arcgisonline.com/arcgis/rest/services/Specialty/DeLorme_World_Base_Map/MapServer
         *
         * meatdata example:
         * url + ?f=pjson
         *
         * tile example:
         * url + /tile/{z}/{y}/{x}
         */
        key: 'arcgisrestmap',
        rule: /(arcgisrestmap)|(^4$)/i,
        async getMDObj() {
            return {}
        },
        createLayer(
            svcUrl: string,
            props: L.TileLayerOptions = {}
        ): L.TileLayer {
            return L.tileLayer(svcUrl + '/tile/{z}/{y}/{x}', props)
        },
        createLayer3d(svcUrl: string, props: any = {}): Cesium.ImageryLayer {
            return new Cesium.ImageryLayer(
                new Cesium.ArcGisMapServerImageryProvider({
                    url: svcUrl,
                    ...props,
                }),
                {
                    alpha: props.opacity ?? 1,
                }
            )
        },
    },
]

async function matchAndCreate(
    svcType: string,
    svcMDUrl: string,
    props?: any,
    isGlobal: Boolean = false
) {
    const setting = settings.service || {}
    const extendServices = setting.extendServices! || []

    // 读取外部和内部类型
    const matchService = [...extendServices, ...SERVICES].find(s =>
        typeof s.rule === 'function' ? s.rule(svcType) : s.rule.test(svcType)
    )
    if (!matchService) throw '不支持的服务类型！'

    const layerCreater = isGlobal
        ? matchService.createLayer3d
        : matchService.createLayer

    if (!layerCreater) throw '当前地图不支持该服务类型！'

    const url = parseServiceUrl(svcMDUrl)

    // 元数据请求（不做错误处理）
    const mdprops = await matchService.getMDObj(url).catch(err => {
        Error('服务元数据请求失败', err)
        return {}
    })

    // 创建图层
    return layerCreater(url, { ...mdprops, ...props })
}

// 创建服务 --- 2d
export async function createService(
    svcType: string,
    svcMDUrl: string,
    props?: any
): Promise<L.Layer> {
    return matchAndCreate(svcType, svcMDUrl, props, false) as any
}

// 创建服务 --- 3d
export function createService3d(
    svcType: string,
    svcMDUrl: string,
    props?: any
): Promise<Cesium.ImageryLayer> {
    return matchAndCreate(svcType, svcMDUrl, props, true) as any
}

// 服务url转换
export function parseServiceUrl(url: string): string {
    const setting = settings.service || {}
    return setting.urlParser ? setting.urlParser(url) : url
}
